import { Node, TiledMap, TiledMapAsset, UITransform, v3, Vec2 } from 'cc';
import { app } from 'db://app-game/core/app';
import { ViewUtil } from 'db://app-game/core/utils/ViewUtil';
import {
  ColliderParams,
  MonitorGroupComp,
} from 'db://app-game/libs/ecs/common/model/MonitorGroupComp';
import { ecs } from 'db://app-game/libs/ecs/ECS';
import { Graph } from 'db://app-game/libs/math/graph/Graph';
import FlowGrid from 'db://app-game/libs/math/pathfinding/flow-field/FlowGrid';
import Simulator from 'db://app-game/libs/math/rov2/Simulator';
import { GamePrefab } from '../../../common/config/GamePrefab';
import { HomeBase } from '../home-base/HomeBase';
import { Lineup } from './LevelMap.type';
import { LevelMapModelComp } from './model/LevelMapModelComp';
import { LevelMapModelRenderComp } from './model/LevelMapRenderModelComp';

/** 关卡地图 */
@ecs.register('LevelMap')
export class LevelMap extends ecs.Entity {
  // 数据层
  LevelMapModel!: LevelMapModelComp;

  // 业务层
  LevelMapModelRender!: LevelMapModelRenderComp; // 地图渲染组件
  MonitorGroup!: MonitorGroupComp; // 监视器组件
  /** 避障模拟器实例 */
  simulator!: Simulator;

  // 视图层
  /** 地图节点 */
  mapNode!: Node;
  /** 怪兽工厂节点 */
  monsterFactoryNode!: Node;
  /** 角色工厂节点 */
  roleFactoryNode!: Node;
  /** 静态生物工厂节点 */
  staticCreatureFactoryNode!: Node;
  /** 特效工厂节点 */
  effectFactoryNode!: Node;
  init() {
    // 1. 添加避障模拟器
    this.simulator = new Simulator();
    // 2. 设置帧率
    this.simulator.setTimeStep(0.5);
    // 3.设置默认代理参数
    this.simulator.setAgentDefaults(
      //在寻找周围邻居的搜索距离，这个值设置越大，会让小球在越远距离做出避障行为
      100, // neighbor distance (min = radius * radius)

      //寻找周围邻居的最大数目，这个值设置越大，最终计算的速度越 精确，但会加大计算量
      10, // max neighbors

      //计算动态的物体时的时间窗口
      100, // time horizon

      //代表计算静态的物体时的时间窗口，比如在RTS游戏中，小兵 向城墙移动时，没必要做出避障，这个值需要设置的很
      1, // time horizon obstacles

      //代表计算ORCA时的小球的半径，这个值不一定与小球实际显示的半径 一样，偏小有利于小球移动顺畅
      15, // agent radius

      //小球最大速度值
      2 // max speed
      //初始速度
      // 0, // default velocity for x
      // 0, // default velocity for y
    );

    this.addComponents(LevelMapModelComp);
  }

  /** 加载地图节点 */
  load() {
    // 1.创建地图节点
    this.createNode();

    // 2.添加监视器组件
    this.addComponents(MonitorGroupComp);
    this.MonitorGroup.group = [
      {
        name: this.roleFactoryNode.name, // 角色工厂节点名
        monitorNode: this.roleFactoryNode,
      },
      {
        name: this.staticCreatureFactoryNode.name, // 静态生物工厂节点名
        monitorNode: this.staticCreatureFactoryNode,
      },
      {
        name: this.monsterFactoryNode.name, // 怪兽工厂节点名
        monitorNode: this.monsterFactoryNode,
      },
    ];

    // 3.添加渲染地图组件
    this.add(LevelMapModelRenderComp);
    // 4.渲染地图
    this.renderMap(this).then(() => {
      this.LevelMapModelRender.isRender = true;
    });
  }

  /** 创建地面子节点 */
  createNode() {
    // 1.创建地图节点
    this.mapNode = new Node();
    this.mapNode.name = 'map';
    this.mapNode.addComponent(UITransform);
    this.mapNode.parent = app.gui.game;

    // 2.添加怪兽工厂节点
    this.monsterFactoryNode = new Node();
    this.monsterFactoryNode.name = 'monster-factory';
    this.monsterFactoryNode.addComponent(UITransform);
    this.monsterFactoryNode.parent = app.gui.game;

    // 3.添加特效工厂节点
    this.effectFactoryNode = new Node();
    this.effectFactoryNode.name = 'effect-factory';
    this.effectFactoryNode.addComponent(UITransform);
    this.effectFactoryNode.parent = app.gui.game;

    // 4.添加角色工厂节点
    this.roleFactoryNode = new Node();
    this.roleFactoryNode.name = 'role-factory';
    this.roleFactoryNode.addComponent(UITransform);
    this.roleFactoryNode.parent = app.gui.game;

    // 5.添加静态生物工厂节点
    this.staticCreatureFactoryNode = new Node();
    this.staticCreatureFactoryNode.name = 'static-creature-factory';
    this.staticCreatureFactoryNode.addComponent(UITransform);
    this.staticCreatureFactoryNode.parent = app.gui.game;
  }

  /** 创建关卡地图节点 */
  async createMapNode() {
    const node = ViewUtil.createPrefabNode(GamePrefab.LevelMap);
    const tmxAsset = await app.res.loadAsync(this.LevelMapModel.mapFilePath, TiledMapAsset);
    const tileMap = node.addComponent(TiledMap);
    tileMap.tmxAsset = tmxAsset;
    return node;
  }

  /** 渲染地图 */
  async renderMap(levelMap: LevelMap) {
    const { chunkMapWidth, chunkMapHeight, tileWidth } = levelMap.LevelMapModel;
    const lmmr = levelMap.LevelMapModelRender;
    const node = await levelMap.createMapNode();
    node.setPosition(0, 0);

    // 3.设置地图父级节点
    node.parent = levelMap.mapNode;
    const tiledMap = node.getComponent(TiledMap);

    // 4.设置地图
    const centerPoint = new Vec2(0, 0);
    centerPoint.x = node.position.x - chunkMapWidth / 2;
    centerPoint.y = node.position.y + chunkMapHeight / 2;
    lmmr.graph = new Graph([[tiledMap]], tileWidth, 'obstacle', centerPoint);

    // 5.获取双方基地
    // 5.1设置玩家大本营
    let playerBaseGroup = tiledMap.getObjectGroup('playerHomeBase');
    let playerBaseObjects = playerBaseGroup.getObjects();
    if (playerBaseObjects.length > 0) {
      let playerBaseObject = playerBaseObjects[0];
      const pos = v3(
        centerPoint.x + playerBaseObject.offset.x,
        centerPoint.y - playerBaseObject.offset.y
      );
      lmmr.playerHomeBase = ecs.getEntity<HomeBase>(HomeBase);
      lmmr.playerHomeBase.load(levelMap.mapNode, pos, levelMap.roleFactoryNode, Lineup.Player);
    }

    // 5.2设置ai大本营
    let aiBaseGroup = tiledMap.getObjectGroup('aiHomeBase');
    let aiBaseObjects = aiBaseGroup.getObjects();
    if (aiBaseObjects.length > 0) {
      let aiBaseObject = aiBaseObjects[0];
      const pos = v3(centerPoint.x + aiBaseObject.offset.x, centerPoint.y - aiBaseObject.offset.y);
      lmmr.aiHomeBase = ecs.getEntity<HomeBase>(HomeBase);
      lmmr.aiHomeBase.load(levelMap.mapNode, pos, levelMap.monsterFactoryNode, Lineup.AI);
    }

    // 6.设置流动网格
    lmmr.flowGridObj.player = new FlowGrid(lmmr.graph);
    lmmr.flowGridObj.ai = new FlowGrid(lmmr.graph);
  }

  /** 碰撞体列表 */
  colliderList(res: ColliderParams) {
    const { quadTreeObj } = this.MonitorGroup;
    const { name, circle } = res;
    if (name && quadTreeObj[name]) {
      return quadTreeObj[name]?.retrieve(circle) || [];
    }
    return [];
  }
}
